<?php
/**
 * @table paysystems
 * @id a6it
 * @title A6iT
 * @visible_link http://www.a6it.com/
 * @recurring cc
 * @am_payment_api 6.0
 */
class Am_Paysystem_A6it extends Am_Paysystem_CreditCard //Am_Paysystem_Abstract
{

    const PLUGIN_STATUS = self::STATUS_DEV;
    const PLUGIN_DATE = '$Date$';
    const PLUGIN_REVISION = '6.3.32';

    protected $defaultTitle = "A6iT";
    protected $defaultDescription = "accepts credit cards";
    protected $_pciDssNotRequired = true;

    const INVOICE_CUSTOMER_CODE = 'a6it-customer-code';

    public function _initSetupForm(Am_Form_Setup $form)
    {
        $fs = $form->addFieldset()
            ->setLabel("Account Settings");

        $fs->addText("merchant_id")
            ->setLabel("Merchant ID as Supplied")
            ->addRule('required');

        $fs->addText("user_id")
            ->setLabel("User ID as Supplied")
            ->addRule('required');

        $fs->addSecretText("password")
            ->setLabel("Password as Supplied")
            ->addRule('required');

        $fs->addText("secure_key")
            ->setLabel("Secure Key")
            ->addRule('required');

        $fs = $form->addFieldset()
            ->setLabel("Gateway Settings");

        $fs->addText("hosted_page", ['size' => 40])
            ->setLabel("Hosted Page \n" .
                "full URL to hosted page")
            ->addRule('required');

        $fs->addText("recurring_page", ['size' => 40])
            ->setLabel("Recurring Page\n" .
                "full URL to recurring page")
            ->addRule('required');

        $fs = $form->addFieldset()
            ->setLabel("Additional Settings");

        $fs->addAdvCheckbox("debugLog")
            ->setLabel("Debug Log Enabled\n" .
                "write all requests/responses to log");
    }

    public function isConfigured()
    {
        return (bool)$this->getConfig('user_id');
    }

    public function loadCreditCard(Invoice $invoice)
    {
        return $this->getDi()->CcRecordTable->createRecord(); // return fake record for rebill
    }

    public function _doBill(Invoice $invoice, $doFirst, CcRecord $cc, Am_Paysystem_Result $result)
    {
        if (!$doFirst)
        {
            $customerCode = $invoice->data()->get(self::INVOICE_CUSTOMER_CODE);
            if (!$customerCode)
                return $result->setFailed(["No saved customer code for invoice"]);

            $tr = new Am_Paysystem_Transaction_CreditCard_A6itRebill($this, $invoice, $customerCode);
            $tr->run($result);
        }
    }

    public function _process($invoice, $request, $result)
    {
        $user = $invoice->getUser();
        $post = [
            'Merchantid' => $this->getConfig('merchant_id'),
            'type' => 1, // Payment
            'userid' => $this->getConfig('user_id'),
            'password' => $this->getConfig('password'),
            'ip' => $this->getDi()->request->getClientIp(),
            'name' => $user->name_f,
            'surname' => $user->name_l,
            'email' => $user->email,
            'phone' => $user->phone,
//            'address' => $user->???,
            'street' => $user->street,
            'city' => $user->city,
            'zip' => $user->zip,
            'country' => $this->getDi()->countryTable->getTitleByCode($user->country),
            'state' => $this->getDi()->stateTable->getTitleByCode($user->country, $user->state),
            'productdescription' => $invoice->getLineDescription(),
            'merchanttransid' => $invoice->public_id,
            'price' => $invoice->first_total,
            'currency' => $invoice->currency,
            'returning_customer' => 'yes',

            'returnurl' => $this->getPluginUrl('status'),
            'cancelurl' => $this->getCancelUrl(),
        ];

        if ($this->getConfig('debugLog'))
            $this->getDi()->logger->error("A6iT [request host-page]: " . json_encode($post));

        $a = new Am_Paysystem_Action_Form($this->getConfig('hosted_page'));
        foreach ($post as $key => $value)
            $a->$key = $value;

        $result->setAction($a);
    }

    public function directAction($request, $response, array $invokeArgs)
    {
        if ($request->getActionName() == 'status')
        {
            if ($this->getConfig('debugLog'))
                $this->getDi()->logger->error("A6iT [response host-page]: " . json_encode($request->getParams()));

            $sign = $this->getConfig('secure_key')
                . $request->get('transid')
                . $request->get('status')
                . $request->get('merchanttransid')
                . $request->get('customer_code');
            if (md5($sign) != $request->get('securesumm'))
                throw new Am_Exception_InputError("A6iT [response host-page]: Bad sign!");

            $this->invoice = $this->getDi()->invoiceTable->findFirstByPublicId($request->get('merchanttransid'));
            if($customerCode = $request->get('customer_code'))
            {
                if ($this->getConfig('debugLog'))
                    $this->getDi()->logger->error("A6iT [customer_code]: " . $customerCode);
                $this->invoice->data()->set(self::INVOICE_CUSTOMER_CODE, $customerCode)->update();
            }

            if (!(float)$this->invoice->first_total) // first - free
            {
                $transactionFree = new Am_Paysystem_Transaction_Free($this);
                $transactionFree->setInvoice($this->invoice);
                $transactionFree->process();
            } else
            {
                $transaction = new Am_Paysystem_Transaction_Incoming_A6it($this, $request, $response, $invokeArgs);
                try
                {
                    $transaction->process();
                } catch (Am_Exception_Paysystem_TransactionAlreadyHandled $e)
                {
                } catch (Exception $e)
                {
                    $this->getDi()->logger->error("Error in a5it direct action", ["exception" => $e]);
                    $response->setRedirect($this->getCancelUrl());
                    return;
                }
            }

            $response->setRedirect($this->getReturnUrl());
        } else
            parent::directAction($request, $response, $invokeArgs);
    }

    public function processRefund(InvoicePayment $payment, Am_Paysystem_Result $result, $amount)
    {
        $customerCode = $payment->getInvoice()->data()->get(self::INVOICE_CUSTOMER_CODE);
        if (!$customerCode)
            return $result->setFailed(["No saved customer code for invoice"]);

        $refundData = [
            'transid' => $payment->receipt_id,
            'amount' => $amount
        ];
        $tr = new Am_Paysystem_Transaction_CreditCard_A6itRefund($this, $payment->getInvoice(), $customerCode, $refundData);
        $tr->run($result);
    }


}

class Am_Paysystem_Transaction_Incoming_A6it extends Am_Paysystem_Transaction_Incoming
{
    public function findInvoiceId()
    {
        return $this->request->getFiltered('merchanttransid');
    }

    public function getUniqId()
    {
        return $this->request->getFiltered('transid');
    }

    public function validateSource()
    {
        return true;
    }

    public function validateStatus()
    {
        return ($this->request->getInt('status') == 1000);
    }

    public function validateTerms()
    {
        return true;
    }
}

class Am_Paysystem_Transaction_CreditCard_A6itRebill extends Am_Paysystem_Transaction_CreditCard
{
    protected $parsedResponse = [];

    public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $customerCode)
    {
        $request = new Am_HttpRequest();
        $request->setHeader('Expect', '');
        parent::__construct($plugin, $invoice, $request, false);

        $this->request->setUrl($this->plugin->getConfig('recurring_page'));
        $this->request->setMethod(Am_HttpRequest::METHOD_POST);

        $user = $this->invoice->getUser();
        $post = [
            'Merchantid' => $this->plugin->getConfig('merchant_id'),
            'type' => 3, // 1 - Payment, 2 - Refund, 3 - Rebill
            'userid' => $this->plugin->getConfig('user_id'),
            'password' => $this->plugin->getConfig('password'),
            'ip' => Am_Di::getInstance()->request->getClientIp(),
            'name' => $user->name_f,
            'surname' => $user->name_l,
            'email' => $user->email,
            'phone' => $user->phone,
//            'address' => $user->???,
            'street' => $user->street,
            'city' => $user->city,
            'zip' => $user->zip,
            'country' => Am_Di::getInstance()->countryTable->getTitleByCode($user->country),
            'state' => Am_Di::getInstance()->stateTable->getTitleByCode($user->country, $user->state),
            'productdescription' => $invoice->getLineDescription(),
            'merchanttransid' => $invoice->public_id . '-' . Am_Di::getInstance()->security->randomString(4),
            'price' => $this->invoice->second_total,
            'currency' => $this->invoice->currency,

            'customer_code' => $customerCode,
        ];

        $this->getPostData($post);

        if ($this->plugin->getConfig('debugLog'))
            Am_Di::getInstance()->logger->error("A6iT [request recurring-page]: " . json_encode($post));

        $this->request->addPostParameter($post);
    }

    protected function getPostData(&$post)
    {
    }

    public function findInvoiceId()
    {
        return $this->parsedResponse->merchanttransid;
    }

    public function getUniqId()
    {
        return $this->parsedResponse->transid;
    }

    public function validateSource()
    {
        return true;
    }

    public function validate()
    {
        switch (intval($this->parsedResponse->status))
        {
            case 2:
                $err = "Invalid user info";
                break;
            case 3:
                $err = "Invalid message structure.";
                break;
            case 4:
                $err = "General exception.";
                break;
            case 5:
                $err = "Blacklisted.";
                break;
            case 6:
                $err = "Invalid previous transaction id.";
                break;
            case 7:
                $err = "Card type not accepted.";
                break;
            case 8:
                $err = "General bank exception.";
                break;
            case 9:
                $err = "Invalid card CVV.";
                break;
            case 10:
                $err = "Invalid card number.";
                break;
            case 11:
                $err = "Invalid card expiry.";
                break;
            case 13:
                $err = "Connection to bank failed.";
                break;
            case 14:
                $err = "Account is blocked.";
                break;

            case 1000:
                break;

            default:
                $err = "Unknown error num: " . $this->parsedResponse->status . ".";
                break;
        }
        if (isset($err))
            return $this->result->setFailed([$err, $this->parsedResponse->error, $this->parsedResponse->report]);

        $this->result->setSuccess($this);
    }

    public function validateTerms()
    {
        return true;
    }

    public function parseResponse()
    {
        parse_str($this->response->getBody(), $this->parsedResponse);
        if ($this->plugin->getConfig('debugLog'))
            Am_Di::getInstance()->logger->error("A6iT [response recurring-page]: " . json_encode($this->parsedResponse));
        $this->parsedResponse = (object)$this->parsedResponse;
    }

    public function getAmount()
    {
        return $this->invoice->second_total;
    }
}

class Am_Paysystem_Transaction_CreditCard_A6itRefund extends Am_Paysystem_Transaction_CreditCard_A6itRebill
{
    protected $parsedResponse = [];
    private $refundData;


    public function __construct(Am_Paysystem_Abstract $plugin, Invoice $invoice, $customerCode, $refundData)
    {
        $this->refundData = $refundData;
        parent::__construct($plugin, $invoice, $customerCode);
    }

    protected function getPostData(&$post)
    {
        $post['type'] = 2; // Refund
        $post['transid'] = $this->refundData['transid'];
        $post['price'] = $this->refundData['amount'];
    }

    public function processValidated(){}

    public function getAmount()
    {
        return $this->refundData['amount'];
    }

}
